#pragma once

#include<iostream>
#include<string>
#include<functional>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"Log.hpp"
#include"InetAddr.hpp"

using namespace LogModule;

using func_t = std::function<std::string(const std::string&, InetAddr&)>;

const int defaultfd = -1;

class UdpServer
{
public:
	UdpServer(uint16_t port, func_t func)
		: _sockfd(defaultfd)
		, _port(port)
		, _isrunning(false)
		, _func(func)
	{
	}

	void Init()
	{
		// 1. 创建套接字
		_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
		if(_sockfd < 0)
		{
			LOG(LogLevel::FATAL) << "socket error!";
			exit(1);
		}
		LOG(LogLevel::INFO) << "socket success, sockfd : " << _sockfd;

		// 2. 绑定socket信息，ip和端口
        // 2.1 填充sockaddr_in结构体
		struct sockaddr_in local;
		bzero(&local, sizeof(local));
		local.sin_family = AF_INET;
		// 我会不会把我的IP地址和端口号发送给对方？
        // IP信息和端口信息，一定要发送到网络！
        // 本地格式->网络序列
		local.sin_port = htons(_port);
		// IP也是如此，1. IP转成4字节 2. 4字节转成网络序列 -> in_addr_t inet_addr(const char *cp);
        //local.sin_addr.s_addr = inet_addr(_ip.c_str()); // TODO
		//全设为0是为了让服务器监听所有可用的网络接口
		local.sin_addr.s_addr = INADDR_ANY;

		// 那么为什么服务器端要显式的bind呢？IP和端口必须是众所周知且不能轻易改变的！
		int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
		if(n < 0)
		{
			LOG(LogLevel::FATAL) << "bind error";
			exit(2);
		}
		LOG(LogLevel::INFO) << "bind success, ssockfd: " << _sockfd;
	}

	void Start()
	{
		_isrunning = true;
		while(_isrunning)
		{
			char buffer[1024];//peer 通常指通信对端
			struct sockaddr_in peer;//服务器的peer是客户端;客户端的peer是服务器
			socklen_t len = sizeof(peer);
			// 1. 收消息, client为什么要个服务器发送消息啊？不就是让服务端处理数据
			ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer - 1), 0, (struct sockaddr*)&peer, &len);
			if(s > 0)//成功：返回实际接收的字节数
			{
				InetAddr client(peer);
				buffer[s] = 0;//设置\0
				// 收到的内容，当做英文单词
				//@2用于传递客户端的网络地址信息
				std::string result = _func(buffer, client);
				//@2传一个自定义类：将底层实现细节隐藏，仅暴露必要的接口

				// 2. 发消息
				sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);
			}
		}
	}

	~UdpServer()
    {
    }

private:
	int _sockfd;
	uint16_t _port;
	bool _isrunning;

	func_t _func;// 服务器的回调函数，用来进行对数据进行处理
};